package com.hup.sokoban.view.customView.mapListView;

import com.hup.sokoban.model.common.MapItem;
import com.hup.utils.commons.NameSorter;
import com.hup.utils.commons.hardware.MouseUtil;
import com.hup.utils.commons.start.LookThemeUtil;
import com.hup.utils.swing.model.DefaultListModel2;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;

/**
 * 关卡列表组件
 *
 * @author hugan
 * @date 2020/4/29
 */
@Log4j2
public class MapListView extends JPanel {
    private JPanel root;
    private JComboBox cbOrder;
    private JList<MapItem> lstMapItem;
    private JLabel labNoneItem;
    private JScrollPane spMapItem;
    private ItemMenu menu;
    private PreviewWindow previewWindow = new PreviewWindow();

    /**
     * 根据名字(正序)还是修改日期(倒序)排序
     */
    @Getter
    private boolean orderByName = true;
    /**
     * true-设计模式[菜单:加载,删除];false-用户模式[菜单:加载]
     */
    @Getter
    private boolean isDesigner = false;

    /**
     * 使用这两个变量实现item的选中和鼠标悬停效果(不深究JList的繁琐原理了)
     */
    private int curSelectedIndex = 0;
    private int curMouseHoverIndex = -1;
    @Setter
    private MenuListener menuListener;

    public MapListView() {
        Dimension dimension = new Dimension((int) (150 * LookThemeUtil.getScreenScale()), -1);
        setPreferredSize(dimension);
        setMinimumSize(dimension);
        $$$setupUI$$$();

        menu = new ItemMenu();

        lstMapItem.setModel(new DefaultListModel2<>());
        lstMapItem.setCellRenderer(new ListCellRenderer<MapItem>() {
            //Renderer:仅用一个Component对象,实现整个list的图像渲染;不能处理鼠标功能的
            MapListItemView mapItemView = new MapListItemView();

            @Override
            public Component getListCellRendererComponent(JList<? extends MapItem> list, MapItem value, int index,
                    boolean isSelected, boolean cellHasFocus) {
                if (value != null) {
                    isSelected = index == curSelectedIndex;
                    cellHasFocus = index == curMouseHoverIndex;
                    log.debug("value={}, isSelected={}, cellHasFocus={}", value.getName(), isSelected, cellHasFocus);
                    mapItemView.setMapItem(lstMapItem, value, index, isSelected, cellHasFocus);
                }
                return mapItemView;
            }
        });
        lstMapItem.setBackground(getBackground());

        initListener();
        //初始化默认空数据
        setMapItems(new ArrayList<>());
        setDesigner(isDesigner);
    }

    public void setDesigner(boolean designer) {
        isDesigner = designer;
        menu.updateIsDesigner();
    }

    private void initListener() {
        cbOrder.addActionListener(e -> setOrderByName(cbOrder.getSelectedIndex() == 0));

        /*
        .实现鼠标移动修改背景色
        .预览窗口的展示逻辑
        列表菜单监听,在 ItemMenu 中添加
        */
        MouseAdapter ma = new MouseAdapter() {
            int lastPreviewIndex;//上次预览的item

            @Override
            public void mouseExited(MouseEvent e) {
                log.debug("");
                curMouseHoverIndex = -1;
                lstMapItem.repaint();
                if (previewWindow.isVisible()) previewWindow.dispose();
            }

            @Override
            public void mouseMoved(MouseEvent e) {
                int hoverIndex = lstMapItem.locationToIndex(e.getPoint());
                if (hoverIndex != curMouseHoverIndex) lstMapItem.repaint();
                curMouseHoverIndex = hoverIndex;

                if (previewWindow.isVisible()) showWindow(e);
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                if (SwingUtilities.isLeftMouseButton(e) && MouseUtil.isInsideComponent(lstMapItem, e)) {
                    if (!previewWindow.isVisible()) {
                        lastPreviewIndex = -1;
                        showWindow(e);
                    } else {
                        previewWindow.dispose();
                    }
                }
            }

            private void showWindow(MouseEvent e) {
                Point point = e.getPoint();
                int indexOfItem = lstMapItem.locationToIndex(point);

                Rectangle cellBounds = lstMapItem.getCellBounds(indexOfItem, indexOfItem);
                if (lastPreviewIndex == indexOfItem) return;

                if (e.getY() > cellBounds.y + cellBounds.height) {
                    //点击列表底部空白区域时,不需展示弹窗
                    log.debug("cellBounds={}, point={}", cellBounds, point);
                    return;
                }

                MapItem mapItem = lstMapItem.getModel().getElementAt(indexOfItem);
                previewWindow.show(mapItem, MapListView.this);
                lastPreviewIndex = indexOfItem;
            }
        };
        lstMapItem.addMouseListener(ma);
        lstMapItem.addMouseMotionListener(ma);

        lstMapItem.registerKeyboardAction(e -> {
            if (previewWindow.isVisible()) previewWindow.dispose();
        }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    }

    public void setOrderByName(boolean orderByName) {
        if (this.orderByName == orderByName) return;

        this.orderByName = orderByName;
        cbOrder.setSelectedIndex(orderByName ? 0 : 1);

        DefaultListModel2<MapItem> model = (DefaultListModel2<MapItem>) lstMapItem.getModel();
        MapItem selectedItem = null;
        if (curSelectedIndex != -1) {
            //处理当前选中的item
            selectedItem = model.elementAt(curSelectedIndex);
        }
        if (orderByName) {
            model.sortByName(MapItem::getName);
        } else {
            model.sort(Comparator.comparing(MapItem::getLastModified));
        }
        curSelectedIndex = model.indexOf(selectedItem);
        if (curSelectedIndex != -1) {
            //滚动到指定位置
            ensureCurVisible();
        } else {
            lstMapItem.repaint();
        }
        log.debug("排序");
    }

    /**
     * @param items notNull,长度可以为0
     */
    public void setMapItems(List<MapItem> items) {
        boolean empty = items.isEmpty();
        cbOrder.setEnabled(!empty);
        spMapItem.setVisible(!empty);
        labNoneItem.setVisible(empty);

        curSelectedIndex = -1;
        curMouseHoverIndex = -1;

        //对数据排序后才添加到JList中(不能直接对传入对象排序!)
        ArrayList<MapItem> temp = new ArrayList<>(items);
        temp.sort(orderByName ? NameSorter.getComparator(items, MapItem::getName) : Comparator.comparing(MapItem::getLastModified));
        ((DefaultListModel2<MapItem>) lstMapItem.getModel()).addAll(temp, true);
    }

    public List<MapItem> getMapItems() {
        DefaultListModel2<MapItem> model = (DefaultListModel2<MapItem>) lstMapItem.getModel();
        return model.getDelegate();
    }

    /**
     * @param curMap 不是本model中的元素:避免用户绘制地图时,列表也跟着刷新;使用path属性作匹配
     */
    public void setSelectedItem(MapItem curMap) {
        Enumeration<MapItem> enumeration = ((DefaultListModel2<MapItem>) lstMapItem.getModel()).elements();
        curSelectedIndex = -1;
        int i = 0;
        while (enumeration.hasMoreElements()) {
            MapItem item = enumeration.nextElement();
            if (item.getPath().equals(curMap.getPath())) {
                curSelectedIndex = i;
                break;
            }
            i++;
        }
        log.debug("curSelectedIndex={}", curSelectedIndex);
        if (curSelectedIndex != -1) {
            //滚动到指定位置
            ensureCurVisible();
        } else {
            lstMapItem.repaint();
        }
    }

    public int getMapCount() {
        return lstMapItem.getModel().getSize();
    }

    public void clearMapItems() {
        ((DefaultListModel2<MapItem>) lstMapItem.getModel()).clear();
    }

    private void createUIComponents() {
        root = this;
    }

    /**
     * @param i [0,size)不能越界
     */
    public void setSelectedIndex(int i) {
        if (i < 0 && i > lstMapItem.getModel().getSize()) {
            throw new IndexOutOfBoundsException("Index: " + i + ", Size: " + lstMapItem.getModel().getSize());
        }
        curSelectedIndex = i;
        ensureCurVisible();
    }

    private void ensureCurVisible() {
        lstMapItem.ensureIndexIsVisible(curSelectedIndex);
        lstMapItem.repaint();
    }

    /**
     * Method generated by IntelliJ IDEA GUI Designer
     * >>> IMPORTANT!! <<<
     * DO NOT edit this method OR call it in your code!
     *
     * @noinspection ALL
     */
    private void $$$setupUI$$$() {
        createUIComponents();
        root.setLayout(new GridBagLayout());
        root.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), null));
        final JLabel label1 = new JLabel();
        label1.setHorizontalAlignment(0);
        label1.setText("关卡");
        GridBagConstraints gbc;
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = 2;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        root.add(label1, gbc);
        final JLabel label2 = new JLabel();
        label2.setText("排序");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.insets = new Insets(0, 3, 0, 5);
        root.add(label2, gbc);
        cbOrder = new JComboBox();
        cbOrder.setEnabled(false);
        final DefaultComboBoxModel defaultComboBoxModel1 = new DefaultComboBoxModel();
        defaultComboBoxModel1.addElement("关卡名");
        defaultComboBoxModel1.addElement("修改日期");
        cbOrder.setModel(defaultComboBoxModel1);
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        root.add(cbOrder, gbc);
        spMapItem = new JScrollPane();
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 3;
        gbc.gridwidth = 2;
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        root.add(spMapItem, gbc);
        lstMapItem = new JList();
        spMapItem.setViewportView(lstMapItem);
        labNoneItem = new JLabel();
        labNoneItem.setHorizontalAlignment(0);
        labNoneItem.setText("无数据");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 2;
        gbc.gridwidth = 2;
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        root.add(labNoneItem, gbc);
    }

    /**
     * @noinspection ALL
     */
    public JComponent $$$getRootComponent$$$() { return root; }

    /**
     * 列表菜单
     */
    private class ItemMenu extends JPopupMenu implements ActionListener {
        private final JMenuItem itemLoad, itemUseMap, itemDelete;
        /**
         * 两个变量:菜单展示前赋值,菜单action后置空
         */
        private int indexOfMenu = -1;
        private MapItem mapOfMenu;

        private ItemMenu() {
            itemLoad = new JMenuItem("加载");
            itemUseMap = new JMenuItem("引用地图");
            itemDelete = new JMenuItem("删除");
            add(itemLoad).addActionListener(this);
            add(itemUseMap).addActionListener(this);
            add(itemDelete).addActionListener(this);

            lstMapItem.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseReleased(MouseEvent e) {
                    //添加监听后才展示菜单
                    if (SwingUtilities.isRightMouseButton(e)
                            && MouseUtil.isInsideComponent(lstMapItem, e)
                            && menuListener != null) {
                        indexOfMenu = lstMapItem.locationToIndex(e.getPoint());
                        log.debug("indexOfMenu={}", indexOfMenu);
                        mapOfMenu = lstMapItem.getModel().getElementAt(indexOfMenu);
                        menu.show(e.getComponent(), e.getX(), e.getY());
                    }
                }

                @Override
                public void mouseClicked(MouseEvent e) {
                    //双击加载
                    if (MouseUtil.isLeftButton(e)
                            && e.getClickCount() == 2
                            && menuListener != null) {
                        indexOfMenu = lstMapItem.locationToIndex(e.getPoint());
                        log.debug("indexOfMenu={}", indexOfMenu);
                        mapOfMenu = lstMapItem.getModel().getElementAt(indexOfMenu);
                        previewWindow.dispose();
                        actionPerformed(new ActionEvent(menu.itemLoad, ActionEvent.ACTION_PERFORMED, null));
                    }
                }
            });
        }

        private void updateIsDesigner() {
            itemUseMap.setVisible(isDesigner);
            itemDelete.setVisible(isDesigner);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            //menuListener为空时,是不会展示菜单的
            if (e.getSource() == itemLoad) {
                boolean success = menuListener.beforeLoadItem(mapOfMenu);
                if (success) {
                    curSelectedIndex = indexOfMenu;
                    //定位到选中项,需要invokeLater才能避免其他组件移位时遮挡选中项
                    SwingUtilities.invokeLater(MapListView.this::ensureCurVisible);
                }
            } else if (e.getSource() == itemUseMap) {
                menuListener.useMapData(mapOfMenu);
            } else {
                boolean delete = menuListener.beforeDeleteItem(mapOfMenu);
                if (delete) {
                    if (curSelectedIndex == indexOfMenu) curSelectedIndex = -1;
                    ((DefaultListModel2) lstMapItem.getModel()).remove(indexOfMenu);

                    boolean empty = ((DefaultListModel2) lstMapItem.getModel()).isEmpty();
                    if (empty) {
                        cbOrder.setEnabled(false);
                        spMapItem.setVisible(false);
                        labNoneItem.setVisible(true);
                    }
                }
            }
            mapOfMenu = null;
            indexOfMenu = -1;
        }
    }

    public interface MenuListener {
        /**
         * @return 是否加载成功:用于切换列表选区
         */
        boolean beforeLoadItem(MapItem mapItem);

        /**
         * 点击了[引用地图]
         */
        default void useMapData(MapItem mapItem) {}

        /**
         * @return 是否删除
         */
        default boolean beforeDeleteItem(MapItem mapItem) {
            return false;
        }
    }

}
